home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / imapd / imapd.c < prev    next >
C/C++ Source or Header  |  1996-02-20  |  43KB  |  1,430 lines

  1. /*
  2.  * Program:    IMAP2bis server
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    5 November 1990
  13.  * Last Edited:    21 February 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. /* Parameter files */
  37.  
  38. #include <sys/time.h>        /* must be before osdep.h */
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include <stdio.h>
  42. #include <ctype.h>
  43. #include <netdb.h>
  44. #include <errno.h>
  45. extern int errno;        /* just in case */
  46. #include <signal.h>
  47. #include <pwd.h>
  48. #include <sys/file.h>
  49. #include <sys/stat.h>
  50. #include "misc.h"
  51.  
  52.  
  53. /* Daemon files */
  54. #define ALERTFILE "/etc/imapd.alert"
  55. #define ANOFILE "/etc/anonymous.newsgroups"
  56.  
  57.  
  58. /* Autologout timer */
  59. #define TIMEOUT 60*30
  60.  
  61.  
  62. /* Login tries */
  63. #define LOGTRY 3
  64.  
  65.  
  66. /* Size of temporary buffers */
  67. #define TMPLEN 8192
  68.  
  69.  
  70. /* Server states */
  71.  
  72. #define LOGIN 0
  73. #define SELECT 1
  74. #define OPEN 2
  75. #define LOGOUT 3
  76.  
  77. /* Global storage */
  78.  
  79. char *version = "7.8(100)";    /* version number of this server */
  80. time_t alerttime = 0;        /* time of last alert */
  81. int state = LOGIN;        /* server state */
  82. int mackludge = 0;        /* MacMS kludge */
  83. int trycreate = 0;        /* saw a trycreate */
  84. int anonymous = 0;        /* non-zero if anonymous */
  85. int logtry = LOGTRY;        /* login tries */
  86. long kodcount = 0;        /* set if KOD has happened already */
  87. MAILSTREAM *stream = NIL;    /* mailbox stream */
  88. MAILSTREAM *tstream = NIL;    /* temporary mailbox stream */
  89. long nmsgs = 0;            /* number of messages */
  90. long recent = 0;        /* number of recent messages */
  91. char *host = NIL;        /* local host name */
  92. char *user = NIL;        /* user name */
  93. char *pass = NIL;        /* password */
  94. char *home = NIL;        /* home directory */
  95. char *flags = NIL;        /* flag text */
  96. char cmdbuf[TMPLEN];        /* command buffer */
  97. char *tag;            /* tag portion of command */
  98. char *cmd;            /* command portion of command */
  99. char *arg;            /* pointer to current argument of command */
  100. char *lsterr = NIL;        /* last error message from c-client */
  101. char *response = NIL;        /* command response */
  102. char *litbuf = NIL;        /* buffer to hold literals */
  103.  
  104.  
  105. /* Response texts */
  106.  
  107. char *win = "%s OK %s completed\015\012";
  108. char *altwin = "%s OK %s\015\012";
  109. char *lose = "%s NO %s failed: %s\015\012";
  110. char *misarg = "%s BAD Missing required argument to %s\015\012";
  111. char *badfnd = "%s BAD FIND option unrecognized: %s\015\012";
  112. char *badarg = "%s BAD Argument given to %s when none expected\015\012";
  113. char *badseq = "%s BAD Bogus sequence in %s\015\012";
  114. char *badatt = "%s BAD Bogus attribute list in %s\015\012";
  115. char *badlit = "%s BAD Bogus literal count in %s\015\012";
  116. char *toobig = "%s BAD Command line too long\015\012";
  117. char *nulcmd = "* BAD Null command\015\012";
  118. char *argrdy = "+ Ready for argument\015\012";
  119.  
  120. /* Function prototypes */
  121.  
  122. void main (int argc,char *argv[]);
  123. void clkint ();
  124. void kodint ();
  125. void slurp (char *s,int n);
  126. char inchar ();
  127. void *flush ();
  128. char *snarf (char **arg);
  129. void fetch (char *s,char *t);
  130. void fetch_body (long i,char *s);
  131. void fetch_body_part (long i,char *s);
  132. void fetch_envelope (long i,char *s);
  133. void fetch_encoding (long i,char *s);
  134. void changed_flags (long i,int f);
  135. void fetch_flags (long i,char *s);
  136. void fetch_internaldate (long i,char *s);
  137. void fetch_rfc822 (long i,char *s);
  138. void fetch_rfc822_header (long i,char *s);
  139. void fetch_rfc822_size (long i,char *s);
  140. void fetch_rfc822_text (long i,char *s);
  141. void penv (ENVELOPE *env);
  142. void pbody (BODY *body);
  143. void pstring (char *s);
  144. void paddr (ADDRESS *a);
  145. long cstring (char *s);
  146. long caddr (ADDRESS *a);
  147.  
  148. /* Main program */
  149.  
  150. void main (int argc,char *argv[])
  151. {
  152.   long i;
  153.   char *s,*t,*u,*v,tmp[MAILTMPLEN];
  154.   struct hostent *hst;
  155.   struct stat sbuf;
  156.   void (*f) () = NIL;
  157. #include "linkage.c"
  158.   openlog ("imapd",LOG_PID,LOG_MAIL);
  159.   gethostname (cmdbuf,TMPLEN-1);/* get local name */
  160.   host = cpystr ((hst = gethostbyname (cmdbuf)) ? hst->h_name : cmdbuf);
  161.   rfc822_date (cmdbuf);        /* get date/time now */
  162.   if (i = getuid ()) {        /* logged in? */
  163.     if (!(s = (char *) getlogin ())) s = (getpwuid (i))->pw_name;
  164.     if (i < 0) anonymous = T;    /* must be anonymous if pseudo-user */
  165.     user = cpystr (s);        /* set up user name */
  166.     pass = cpystr ("*");    /* and fake password */
  167.     state = SELECT;        /* enter select state */
  168.     t = "PREAUTH";        /* pre-authorized */
  169.     syslog (LOG_INFO,"Preauthenticated user=%.80s host=%.80s",user,
  170.         tcp_clienthost (tmp));
  171.   }
  172.   else t = "OK";
  173.   printf ("* %s %s IMAP2bis Service %s at %s\015\012",t,host,version,cmdbuf);
  174.   fflush (stdout);        /* dump output buffer */
  175.   signal (SIGALRM,clkint);    /* prepare for clock interrupt */
  176.   signal (SIGUSR2,kodint);    /* prepare for Kiss Of Death */
  177.   do {                /* command processing loop */
  178.     slurp (cmdbuf,TMPLEN-1);    /* slurp command */
  179.                 /* no more last error or literal */
  180.     if (lsterr) fs_give ((void **) &lsterr);
  181.     if (litbuf) fs_give ((void **) &litbuf);
  182.                 /* find end of line */
  183.     if (!strchr (cmdbuf,'\012')) {
  184.       if (t = strchr (cmdbuf,' ')) *t = '\0';
  185.       flush ();            /* flush excess */
  186.       printf (response,t ? cmdbuf : "*");
  187.     }
  188.     else if (!(tag = strtok (cmdbuf," \015\012"))) fputs (nulcmd,stdout);
  189.     else if (!(cmd = strtok (NIL," \015\012")))
  190.       printf ("%s BAD Missing command\015\012",tag);
  191.     else {            /* parse command */
  192.       response = win;        /* set default response */
  193.       ucase (cmd);        /* canonicalize command case */
  194.                 /* snarf argument */
  195.       arg = strtok (NIL,"\015\012");
  196.                 /* LOGOUT command always valid */
  197.       if (!strcmp (cmd,"LOGOUT")) {
  198.     if (state == OPEN) mail_close (stream);
  199.     stream = NIL;
  200.     printf("* BYE %s IMAP2bis server terminating connection\015\012",host);
  201.     state = LOGOUT;
  202.       }
  203.  
  204.                 /* kludge for MacMS */
  205.       else if ((!strcmp (cmd,"VERSION")) && (s = snarf (&arg)) && (!arg) &&
  206.            (i = atol (s)) && (i == 4)) {
  207.     mackludge = T;
  208.     fputs ("* OK [MacMS] The MacMS kludges are enabled\015\012",stdout);
  209.       }
  210.       else if (!strcmp (cmd,"NOOP")) {
  211.     if ((state == OPEN) && !mail_ping (stream)) {
  212.       printf ("* BYE %s Fatal mailbox error: %s\015\012",host,
  213.           lsterr ? lsterr : "<unknown>");
  214.       state = LOGOUT;    /* go away */
  215.       syslog (LOG_INFO,
  216.           "Fatal mailbox error user=%.80s host=%.80s mbx=%.80s: %.80s",
  217.           user ? user : "???",tcp_clienthost (tmp),
  218.           (stream && stream->mailbox) ? stream->mailbox : "???",
  219.           lsterr ? lsterr : "???");
  220.     }
  221.       }
  222.       else switch (state) {    /* dispatch depending upon state */
  223.       case LOGIN:        /* waiting to get logged in */
  224.     if (!strcmp (cmd,"LOGIN")) {
  225.       struct passwd *pwd;
  226.       if (user) fs_give ((void **) &user);
  227.       if (pass) fs_give ((void **) &pass);
  228.                 /* two arguments */
  229.       if (!((user = cpystr (snarf (&arg))) &&
  230.         (pass = cpystr (snarf (&arg))))) response = misarg;
  231.       else if (arg) response = badarg;
  232.                 /* see if username and password are OK */
  233.       else if (server_login (user,pass,&home,argc,argv)) {
  234.         state = SELECT;
  235.         syslog (LOG_INFO,"Login user=%.80s host=%.80s",user,
  236.             tcp_clienthost(tmp));
  237.       }
  238.                 /* nope, see if we allow anonymous */
  239.       else if (!stat (ANOFILE,&sbuf) && !strcmp (user,"anonymous") &&
  240.            (pwd = getpwnam ("nobody"))) {
  241.         anonymous = T;    /* note we are anonymous, login as nobody */
  242.         setgid (pwd->pw_gid);
  243.         setuid (pwd->pw_uid);
  244.         state = SELECT;    /* make selected */
  245.         syslog (LOG_INFO,"Login anonymous=%.80s host=%.80s",pass,
  246.             tcp_clienthost(tmp));
  247.       }
  248.       else {
  249.         response = "%s NO Bad %s user name and/or password\015\012";
  250.         sleep (3);        /* slow the cracker down */
  251.         if (!--logtry) {
  252.           fputs ("* BYE Too many login failures\015\012",stdout);
  253.           state = LOGOUT;
  254.           syslog(LOG_INFO,"Excessive login failures user=%.80s host=%.80s",
  255.              user,tcp_clienthost (tmp));
  256.         }
  257.         else syslog (LOG_INFO,"Login failure user=%.80s host=%.80s",
  258.              user,tcp_clienthost (tmp));
  259.       }
  260.     }
  261.     else response = "%s BAD Command unrecognized/login please: %s\015\012";
  262.     break;
  263.  
  264.       case OPEN:        /* valid only when mailbox open */
  265.                 /* fetch mailbox attributes */
  266.     if (!strcmp (cmd,"FETCH")) {
  267.       if (!(arg && (s = strtok (arg," ")) && (t = strtok(NIL,"\015\012"))))
  268.         response = misarg;
  269.       else fetch (s,t);    /* do the fetch */
  270.     }
  271.                 /* fetch partial mailbox attributes */
  272.     else if (!strcmp (cmd,"PARTIAL")) {
  273.       unsigned long msgno,start,count,size;
  274.       if (!(arg && (msgno = strtol (arg,&s,10)) && (t = strtok (s," ")) &&
  275.         (s = strtok (NIL,"\015\012")) && (start = strtol (s,&s,10)) &&
  276.         (count = strtol (s,&s,10)))) response = misarg;
  277.       else if (s && *s) response = badarg;
  278.       else if (msgno > stream->nmsgs) response = badseq;
  279.       else {        /* looks good */
  280.         int f = mail_elt (stream,msgno)->seen;
  281.         u = s = NIL;    /* no strings yet */
  282.         if (!strcmp (ucase (t),"RFC822")) {
  283.                 /* have to make a temporary buffer for this */
  284.           size = mail_elt (stream,msgno)->rfc822_size;
  285.           s = u = (char *) fs_get (size + 1);
  286.           strcpy (u,mail_fetchheader (stream,msgno));
  287.           strcat (u,mail_fetchtext (stream,msgno));
  288.           u[size] = '\0';    /* tie off string */
  289.         }
  290.         else if (!strcmp (t,"RFC822.HEADER"))
  291.           size = strlen (s = mail_fetchheader (stream,msgno));
  292.         else if (!strcmp (t,"RFC822.TEXT"))
  293.           size = strlen (s = mail_fetchtext (stream,msgno));
  294.         else if (*t == 'B' && t[1] == 'O' && t[2] == 'D' && t[3] == 'Y' &&
  295.              t[4] == '[' && *(t += 5)) {
  296.           if ((v = strchr (t,']')) && !v[1]) {
  297.         *v = '\0';    /* tie off body part */
  298.         s = mail_fetchbody (stream,msgno,t,&size);
  299.           }
  300.         }
  301.         if (s) {        /* got a string back? */
  302.           if (size <= --start) s = NIL;
  303.           else {        /* have string we can make smaller */
  304.         s += start;    /* this is the first byte */
  305.                 /* tie off as appropriate */
  306.         if (count < (size - start)) s[count] = '\0';
  307.           }
  308.           printf ("* %ld FETCH (%s ",msgno,t);
  309.           pstring (s);    /* write the string */
  310.           changed_flags (msgno,f);
  311.           fputs (")\015\012",stdout);
  312.           if (u) fs_give ((void **) &u);
  313.         }
  314.         else response = badatt;
  315.       }
  316.     }
  317.  
  318.                 /* store mailbox attributes */
  319.     else if (!strcmp (cmd,"STORE")) {
  320.                 /* must have three arguments */
  321.       if (!(arg && (s = strtok (arg," ")) && (cmd = strtok (NIL," ")) &&
  322.         (t = strtok (NIL,"\015\012")))) response = misarg;
  323.       else if (!strcmp (ucase (cmd),"+FLAGS")) f = mail_setflag;
  324.       else if (!strcmp (cmd,"-FLAGS")) f = mail_clearflag;
  325.       else if (!strcmp (cmd,"FLAGS")) {
  326.         mail_clearflag (stream,s,flags);
  327.         f = mail_setflag;    /* gross, but rarely if ever done */
  328.       }
  329.       else response = "%s BAD STORE %s not yet implemented\015\012";
  330.       if (f) {        /* if a function was selected */
  331.         (*f) (stream,s,t);    /* do it */
  332.         fetch (s,"FLAGS");    /* get the new flags status */
  333.       }
  334.     }
  335.                 /* check for new mail */
  336.     else if (!strcmp (cmd,"CHECK")) {
  337.                 /* no arguments */
  338.       if (arg) response = badarg;
  339.       else if (anonymous) mail_ping (stream);
  340.       else mail_check (stream);
  341.     }
  342.                 /* expunge deleted messages */
  343.     else if (!(anonymous || strcmp (cmd,"EXPUNGE"))) {
  344.                 /* no arguments */
  345.       if (arg) response = badarg;
  346.       else mail_expunge (stream);
  347.     }
  348.                 /* copy message(s) */
  349.     else if (!(anonymous || strcmp (cmd,"COPY"))) {
  350.       trycreate = NIL;    /* no trycreate status */
  351.       if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  352.       else if (arg) response = badarg;
  353.       else if ((!mail_copy (stream,s,t)) &&
  354.            !(mackludge && trycreate &&
  355.              (mail_create (stream,t)) && (mail_copy (stream,s,t)))) {
  356.         response = lose;    /* in case TRYCREATE failure */
  357.         if (!lsterr) lsterr = cpystr ("No such destination mailbox");
  358.       }
  359.     }
  360.  
  361.                 /* search mailbox */
  362.     else if (!strcmp (cmd,"SEARCH")) {
  363.                 /* one or more arguments required */
  364.       if (!arg) response = misarg;
  365.       else {        /* zap search vector */
  366.                 /* end with a literal argument? */
  367.         while ((*(t = arg + strlen (arg) - 1) == '}') &&
  368.            (s = strrchr (arg,'{')) && response == win) {
  369.                 /* get length of literal, validate */
  370.           if (((i = atol (++s)) < 1) || (TMPLEN - (t++ - cmdbuf) < i + 10))
  371.         response = badlit;
  372.           else {
  373.         *t++ = '\015';    /* reappend CR/LF */
  374.         *t++ = '\012';
  375.         fputs (argrdy,stdout);
  376.         fflush (stdout);/* dump output buffer */
  377.                 /* copy the literal */
  378.         while (i--) *t++ = inchar ();
  379.                 /* get new command tail */
  380.         slurp (t,TMPLEN - (t-cmdbuf) - 1);
  381.                 /* find end of line */
  382.         if (s = strchr (t,'\012')) {
  383.           *s = NIL;    /* tie off line */
  384.           if (*--s == '\015') *s = NIL;
  385.         }
  386.         else flush ();    /* error */
  387.           }
  388.         }
  389.                 /* punt if error */
  390.         if (response != win) break;
  391.                 /* do the search */
  392.         mail_search (stream,arg);
  393.         if (response == win || response == altwin) {
  394.                 /* output search results */
  395.           fputs ("* SEARCH",stdout);
  396.           for (i = 1; i <= nmsgs; ++i)
  397.         if (mail_elt (stream,i)->searched) printf (" %d",i);
  398.           fputs ("\015\012",stdout);
  399.         }
  400.       }
  401.     }
  402.     else            /* fall into select case */
  403.  
  404.       case SELECT:        /* valid whenever logged in */
  405.                 /* select new mailbox */
  406.     if ((!(anonymous || (strcmp (cmd,"SELECT") && strcmp (cmd,"EXAMINE"))))
  407.         || (!strcmp (cmd,"BBOARD"))) {
  408.                 /* single argument */
  409.       if (!(s = snarf (&arg))) response = misarg;
  410.       else if (arg) response = badarg;
  411.       else {
  412.         sprintf (tmp,"%s%s",(*cmd == 'B') ? "*" : "",s);
  413.         recent = -1;    /* make sure we get an update */
  414.         if ((stream = mail_open (stream,tmp,anonymous ? OP_ANONYMOUS : NIL
  415.                      + (*cmd == 'E') ? OP_READONLY : NIL))
  416.         && ((response == win) || (response == altwin))) {
  417.                 /* flush old list */
  418.           if (flags) fs_give ((void **) &flags);
  419.           s = tmp;        /* write flags here */
  420.           *s = '(';        /* start new flag list */
  421.           s[1] = '\0';
  422.           for (i = 0; i < NUSERFLAGS; i++)
  423.         if (t = stream->user_flags[i]) strcat (strcat (s,t)," ");
  424.                 /* append system flags to list */
  425.           strcat (s,"\\Answered \\Flagged \\Deleted \\Seen)");
  426.                 /* output list of flags */
  427.           printf ("* FLAGS %s\015\012",(flags = cpystr (s)));
  428.           kodcount = 0;    /* initialize KOD count */
  429.           state = OPEN;    /* note state open */
  430.                 /* note readonly/readwrite */
  431.           response = stream->rdonly ?
  432.         "%s OK [READ-ONLY] %s completed\015\012" :
  433.           "%s OK [READ-WRITE] %s completed\015\012";
  434.           if (anonymous)
  435.         syslog (LOG_INFO,"Anonymous select of %.80s host=%.80s",
  436.             stream->mailbox,tcp_clienthost (tmp));
  437.         }
  438.         else {        /* nuke if still open */
  439.           if (stream) mail_close (stream);
  440.           stream = NIL;
  441.           state = SELECT;    /* no mailbox open now */
  442.           response = lose;    /* open failed */
  443.         }
  444.       }
  445.     }
  446.  
  447.                 /* APPEND message to mailbox */
  448.     else if (!(anonymous || strcmp (cmd,"APPEND"))) {
  449.       u = v = NIL;        /* init flags/date */
  450.                 /* parse mailbox name */
  451.       if ((s = snarf (&arg)) && arg) {
  452.         if (*arg == '(') {    /* parse optional flag list */
  453.           u = ++arg;    /* pointer to flag list contents */
  454.           while (*arg && (*arg != ')')) arg++;
  455.           if (*arg) *arg++ = '\0';
  456.           if (*arg == ' ') arg++;
  457.         }
  458.                 /* parse optional date */
  459.         if (*arg == '"') v = snarf (&arg);
  460.                 /* parse message */
  461.         if (!arg || (*arg != '{'))
  462.           response = "%s BAD Missing literal in %s\015\012";
  463.         else if (!(isdigit (arg[1]) && (i = strtol (arg+1,NIL,10))))
  464.           response = "%s NO Empty message to %s\015\012";
  465.         else if (!(t = snarf (&arg))) response = misarg;
  466.         else if (arg) response = badarg;
  467.         else {        /* append the data */
  468.           STRING st;
  469.           INIT (&st,mail_string,(void *) t,i);
  470.           if (!mail_append_full (NIL,s,u,v,&st)) {
  471.         response = lose;/* in case TRYCREATE failure */
  472.         if (!lsterr) lsterr = cpystr ("No such destination mailbox");
  473.           }
  474.         }
  475.       }
  476.       else response = misarg;
  477.     }
  478.  
  479.                 /* find mailboxes or bboards */
  480.     else if (!strcmp (cmd,"FIND")) {
  481.       response = "%s OK FIND %s completed\015\012";
  482.                 /* get subcommand and true argument */
  483.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  484.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  485.         response = misarg;    /* missing required argument */
  486.       else if (arg) response = badarg;
  487.       else if (anonymous) {    /* special version for anonymous users */
  488.         if (!strcmp (cmd,"BBOARDS") || !strcmp (cmd,"ALL.BBOARDS"))
  489.           mail_find_all_bboard (NIL,s);
  490.                 /* other commands not supported */
  491.             else response = badfnd;
  492.       }
  493.       else {        /* dispatch based on type */
  494.         if ((!strcmp (cmd,"MAILBOXES"))||(!strcmp (cmd,"ALL.MAILBOXES"))) {
  495.           if (*s == '{') tstream = mail_open (NIL,s,OP_HALFOPEN);
  496.           if (*cmd == 'A') { /* want them all? */
  497.         mail_find_all (NIL,s);
  498.         if (tstream) mail_find_all (tstream,s);
  499.           }
  500.           else {        /* just subscribed */
  501.         mail_find (NIL,s);
  502.         if (tstream) mail_find (tstream,s);
  503.           }
  504.         }
  505.         else if ((!strcmp (cmd,"BBOARDS"))||(!strcmp (cmd,"ALL.BBOARDS"))){
  506.           if (*s == '{') {    /* prepend leading * if remote */
  507.         sprintf (tmp,"*%s",s);
  508.         tstream = mail_open (NIL,tmp,OP_HALFOPEN);
  509.           }
  510.           if (*cmd == 'A') { /* want them all? */
  511.         mail_find_all_bboard (NIL,s);
  512.         if (tstream) mail_find_all_bboard (tstream,s);
  513.           }
  514.           else {        /* just subscribed */
  515.         mail_find_bboards (NIL,s);
  516.         if (tstream) mail_find_bboards (tstream,s);
  517.           }
  518.         }
  519.         else response = badfnd;
  520.         if (tstream) mail_close (tstream);
  521.         tstream = NIL;    /* no more temporary stream */
  522.       }
  523.     }
  524.  
  525.                 /* subscribe to mailbox or bboard */
  526.     else if (!(anonymous || strcmp (cmd,"SUBSCRIBE"))) {
  527.       response = "%s OK SUBSCRIBE %s completed\015\012";
  528.                 /* get subcommand and true argument */
  529.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  530.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  531.         response = misarg;    /* missing required argument */
  532.       else if (arg) response = badarg;
  533.       else {        /* dispatch based on type */
  534.         if (!strcmp (cmd,"MAILBOX")) mail_subscribe (NIL,s);
  535.         else if (!strcmp (cmd,"BBOARD")) mail_subscribe_bboard (NIL,s);
  536.         else response = "%s BAD SUBSCRIBE option unrecognized: %s\015\012";
  537.       }
  538.     }
  539.                 /* unsubscribe to mailbox or bboard */
  540.     else if (!(anonymous || strcmp (cmd,"UNSUBSCRIBE"))) {
  541.       response = "%s OK UNSUBSCRIBE %s completed\015\012";
  542.                 /* get subcommand and true argument */
  543.       if (!(arg && (s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  544.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  545.         response = misarg;    /* missing required argument */
  546.       else if (arg) response = badarg;
  547.       else {        /* dispatch based on type */
  548.         if (!strcmp (cmd,"MAILBOX")) mail_unsubscribe (NIL,s);
  549.         else if (!strcmp (cmd,"BBOARD")) mail_unsubscribe_bboard (NIL,s);
  550.         else response="%s BAD UNSUBSCRIBE option unrecognized: %s\015\012";
  551.       }
  552.     }
  553.                 /* create mailbox */
  554.     else if (!(anonymous || strcmp (cmd,"CREATE"))) {
  555.       if (!(s = snarf (&arg))) response = misarg;
  556.       else if (arg) response = badarg;
  557.       mail_create (NIL,s);    /* create the sucker */
  558.     }
  559.                 /* delete mailbox */
  560.     else if (!(anonymous || strcmp (cmd,"DELETE"))) {
  561.       if (!(s = snarf (&arg))) response = misarg;
  562.       else if (arg) response = badarg;
  563.       else mail_delete (NIL,s);
  564.     }
  565.                 /* rename mailbox */
  566.     else if (!(anonymous || strcmp (cmd,"RENAME"))) {
  567.       if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  568.       else if (arg) response = badarg;
  569.       else mail_rename (NIL,s,t);
  570.     }
  571.  
  572.     else response = "%s BAD Command unrecognized: %s\015\012";
  573.     break;
  574.       default:
  575.         response = "%s BAD Server in unknown state for %s command\015\012";
  576.     break;
  577.       }
  578.       if (state == OPEN) {    /* mailbox open? */
  579.                 /* yes, change in recent messages? */
  580.     if (recent != stream->recent)
  581.       printf ("* %d RECENT\015\012",(recent = stream->recent));
  582.                 /* output changed flags */
  583.     for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->spare2) {
  584.       printf ("* %d FETCH (",i);
  585.       fetch_flags (i,NIL);
  586.       fputs (")\015\012",stdout);
  587.     }
  588.       }
  589.                 /* output any new alerts */
  590.       if (!stat (ALERTFILE,&sbuf) && (sbuf.st_mtime > alerttime)) {
  591.     FILE *alf = fopen (ALERTFILE,"r");
  592.     int c,lc = '\012';
  593.     if (alf) {        /* only if successful */
  594.       while ((c = getc (alf)) != EOF) {
  595.         if (lc == '\012') fputs ("* OK [ALERT] ",stdout);
  596.         switch (c) {    /* output character */
  597.         case '\012':
  598.           fputs ("\015\012",stdout);
  599.         case '\0':        /* flush nulls */
  600.           break;
  601.         default:
  602.           putchar (c);    /* output all other characters */
  603.           break;
  604.         }
  605.         lc = c;        /* note previous character */
  606.       }
  607.       fclose (alf);
  608.       if (lc != '\012') fputs ("\015\012",stdout);
  609.       alerttime = sbuf.st_mtime;
  610.     }
  611.       }
  612.                 /* get text for alternative win message now */
  613.       if (response == altwin) cmd = lsterr;
  614.                 /* output response */
  615.       printf (response,tag,cmd,lsterr);
  616.     }
  617.     fflush (stdout);        /* make sure output blatted */
  618.   } while (state != LOGOUT);    /* until logged out */
  619.   syslog (LOG_INFO,"Logout user=%.80s host=%.80s",user ? user : "???",
  620.       tcp_clienthost (tmp));
  621.   exit (0);            /* all done */
  622. }
  623.  
  624. /* Clock interrupt
  625.  */
  626.  
  627. void clkint ()
  628. {
  629.   char tmp[MAILTMPLEN];
  630.   fputs ("* BYE Autologout; idle for too long\015\012",stdout);
  631.   syslog (LOG_INFO,"Autologout user=%.80s host=%.80s",user ? user : "???",
  632.       tcp_clienthost (tmp));
  633.   fflush (stdout);        /* make sure output blatted */
  634.                 /* try to gracefully close the stream */
  635.   if (state == OPEN) mail_close (stream);
  636.   stream = NIL;
  637.   exit (0);            /* die die die */
  638. }
  639.  
  640.  
  641. /* Kiss Of Death interrupt
  642.  */
  643.  
  644. void kodint ()
  645. {
  646.   char *s;
  647.   if (!kodcount++) {        /* only do this once please */
  648.                 /* must be open for this to work */
  649.     if ((state == OPEN) && stream && stream->dtb && (s = stream->dtb->name) &&
  650.     (!strcmp (s,"bezerk") || !strcmp (s,"mbox") || !strcmp (s,"mmdf"))) {
  651.       fputs("* OK [READ-ONLY] Now READ-ONLY, mailbox lock surrendered\015\012",
  652.         stdout);
  653.       stream->rdonly = T;    /* make the stream readonly */
  654.       mail_ping (stream);    /* cause it to stick! */
  655.     }
  656.     else fputs ("* NO Unexpected KOD interrupt received, ignored\015\012",
  657.         stdout);
  658.     fflush (stdout);        /* make sure output blatted */
  659.   }
  660. }
  661.  
  662. /* Slurp a command line
  663.  * Accepts: buffer pointer
  664.  *        buffer size
  665.  */
  666.  
  667. void slurp (char *s,int n)
  668. {
  669.   alarm (TIMEOUT);        /* get a command under timeout */
  670.   errno = 0;            /* clear error */
  671.   while (!fgets (s,n,stdin)) {    /* read buffer */
  672.     if (errno==EINTR) errno = 0;/* ignore if some interrupt */
  673.     else {
  674.       syslog (LOG_INFO,
  675.           "Connection broken while reading line user=%.80s host=%.80s",
  676.           user ? user : "???",tcp_clienthost (s));
  677.       _exit (1);
  678.     }
  679.   }
  680.   alarm (0);            /* make sure timeout disabled */
  681. }
  682.  
  683.  
  684. /* Read a character
  685.  * Returns: character
  686.  */
  687.  
  688. char inchar ()
  689. {
  690.   char c,tmp[MAILTMPLEN];
  691.   while ((c = getchar ()) == EOF) {
  692.     if (errno==EINTR) errno = 0;/* ignore if some interrupt */
  693.     else {
  694.       syslog (LOG_INFO,
  695.           "Connection broken while reading char user=%.80s host=%.80s",
  696.           user ? user : "???",tcp_clienthost (tmp));
  697.       _exit (1);
  698.     }
  699.   }
  700.   return c;
  701. }
  702.  
  703.  
  704. /* Flush until newline seen
  705.  * Returns: NIL, always
  706.  */
  707.  
  708. void *flush ()
  709. {
  710.   while (inchar () != '\012');    /* slurp until we find newline */
  711.   response = toobig;
  712.   return NIL;
  713. }
  714.  
  715. /* Snarf an argument
  716.  * Accepts: pointer to argument text pointer
  717.  * Returns: argument
  718.  */
  719.  
  720. char *snarf (char **arg)
  721. {
  722.   long i;
  723.   char tmp[TMPLEN];
  724.   char *c = *arg;
  725.   char *s = c + 1;
  726.   char *t = NIL;
  727.   if (!c) return NIL;        /* better be an argument */
  728.   switch (*c) {            /* see what the argument is */
  729.   case '\0':            /* catch bogons */
  730.   case ' ':
  731.     return NIL;
  732.   case '"':            /* quoted string */
  733.     for (c = s; *c != '"'; c++){/* hunt for trailing quote */
  734.       if (*c == '\\') c++;    /* quote next character */
  735.       if (!*c) return NIL;    /* unterminated quoted string! */
  736.     }
  737.     *c++ = '\0';        /* tie off string */
  738.                 /* update argument pointer */
  739.     if (*c) *arg = (*c == ' ') ? c + 1 : c;
  740.     else *arg = NIL;        /* no more arguments */
  741.     return s;
  742.   case '{':            /* literal string */
  743.     if (isdigit (*s)) {        /* be sure about that */
  744.       i = strtol (s,&t,10);    /* get its length */
  745.                 /* validate end of literal */
  746.       if (*t++ != '}' || *t++) return NIL;
  747.       fputs (argrdy,stdout);    /* tell client ready for argument */
  748.       fflush (stdout);        /* dump output buffer */
  749.                 /* get a literal buffer */
  750.       c = litbuf = (char *) fs_get (i+1);
  751.       alarm (TIMEOUT);        /* start timeout */
  752.       while (i--) *c++ = inchar ();
  753.       alarm (0);        /* stop timeout */
  754.       *c++ = NIL;        /* make sure string tied off */
  755.       c = litbuf;        /* return value */
  756.                     /* get new command tail */
  757.       slurp ((*arg = t),TMPLEN - (t - cmdbuf) - 1);
  758.                 /* flush if didn't see NL */
  759.       if (!strchr (t,'\012')) return flush ();
  760.       break;
  761.     }
  762.                 /* otherwise fall through (third party IMAP) */
  763.   default:            /* atomic string */
  764.     c = strtok (c," \015\012");
  765.     break;
  766.   }
  767.                 /* remainder of arguments */
  768.   if ((*arg = strtok (t,"\015\012")) && **arg == ' ') ++*arg;
  769.   return c;
  770. }
  771.  
  772. /* Fetch message data
  773.  * Accepts: sequence as a string
  774.  *        string of data items to be fetched
  775.  */
  776.  
  777. #define MAXFETCH 100
  778.  
  779. void fetch (char *s,char *t)
  780. {
  781.   char c,*v;
  782.   long i,k;
  783.   BODY *b;
  784.   int parse_envs = NIL;
  785.   int parse_bodies = NIL;
  786.   void (*f[MAXFETCH]) ();
  787.   char *fa[MAXFETCH];
  788.                 /* c-client clobbers sequence, use spare */
  789.   if (mail_sequence (stream,s)) for (i = 1; i <= nmsgs; i++)
  790.     mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence;
  791.   else {            /* sequence bogus */
  792.     response = badseq;        /* punt */
  793.     return;
  794.   }
  795.                 /* process macros */
  796.   if (!strcmp (ucase (t),"ALL"))
  797.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)");
  798.   else if (!strcmp (t,"FULL"))
  799.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)");
  800.   else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)");
  801.   s = t + strlen (t) - 1;    /* last character in attribute string */
  802.                 /* if multiple items, make sure in list form */
  803.   if (strchr (t,' ') && ((*t != '(') || (*s != ')'))) {
  804.     response = badatt;
  805.     return;
  806.   }
  807.                 /* nuke the parens now */
  808.   if ((*t == '(') && (*s == ')') && t++) *s = '\0';
  809.   k = 0;            /* initial index */
  810.   if (s = strtok (t," ")) do {    /* parse attribute list */
  811.     if (*s == 'B' && s[1] == 'O' && s[2] == 'D' && s[3] == 'Y') {
  812.                 /* we will need to parse bodies */
  813.       parse_envs = parse_bodies = T;
  814.       switch (s[4]) {
  815.       case '\0':        /* entire body */
  816.     f[k++] = fetch_body;
  817.     break;
  818.       case '[':            /* body segment */
  819.     if ((v = strchr (s + 5,']')) && (!(*v = v[1]))) {
  820.       fa[k] = s + 5;    /* set argument */
  821.       f[k++] = fetch_body_part;
  822.       break;        /* valid body segment */
  823.     }            /* else fall into default case */
  824.       default:            /* bogus */
  825.     response = badatt;
  826.     return;
  827.       }
  828.     }
  829.     else if (!strcmp (s,"ENVELOPE")) {
  830.       parse_envs = T;        /* we will need to parse envelopes */
  831.       f[k++] = fetch_envelope;
  832.     }
  833.     else if (!strcmp (s,"FLAGS")) f[k++] = fetch_flags;
  834.     else if (!strcmp (s,"INTERNALDATE")) f[k++] = fetch_internaldate;
  835.     else if (!strcmp (s,"RFC822")) f[k++] = fetch_rfc822;
  836.     else if (!strcmp (s,"RFC822.HEADER")) f[k++] = fetch_rfc822_header;
  837.     else if (!strcmp (s,"RFC822.SIZE")) f[k++] = fetch_rfc822_size;
  838.     else if (!strcmp (s,"RFC822.TEXT")) f[k++] = fetch_rfc822_text;
  839.     else {            /* unknown attribute */
  840.       response = badatt;
  841.       return;
  842.     }
  843.   } while ((s = strtok (NIL," ")) && k < MAXFETCH);
  844.   else {
  845.     response = misarg;        /* missing attribute list */
  846.     return;
  847.   }
  848.   if (s) {            /* too many attributes? */
  849.     response = "%s BAD Excessively complex FETCH attribute list\015\012";
  850.     return;
  851.   }
  852.   f[k++] = NIL;            /* tie off attribute list */
  853.                 /* for each requested message */
  854.   for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->spare) {
  855.                 /* parse envelope, set body, do warnings */
  856.     if (parse_envs) mail_fetchstructure (stream,i,parse_bodies ? &b : NIL);
  857.     printf ("* %d FETCH (",i);    /* leader */
  858.     (*f[0]) (i,fa[0]);        /* do first attribute */
  859.     for (k = 1; f[k]; k++) {    /* for each subsequent attribute */
  860.       putchar (' ');        /* delimit with space */
  861.       (*f[k]) (i,fa[k]);    /* do that attribute */
  862.     }
  863.     fputs (")\015\012",stdout);    /* trailer */
  864.   }
  865. }
  866.  
  867. /* Fetch message body structure
  868.  * Accepts: message number
  869.  *        string argument
  870.  */
  871.  
  872.  
  873. void fetch_body (long i,char *s)
  874. {
  875.   BODY *body;
  876.   mail_fetchstructure (stream,i,&body);
  877.   fputs ("BODY ",stdout);    /* output attribute */
  878.   pbody (body);            /* output body */
  879. }
  880.  
  881.  
  882. /* Fetch message body part
  883.  * Accepts: message number
  884.  *        string argument
  885.  */
  886.  
  887. void fetch_body_part (long i,char *s)
  888. {
  889.   unsigned long j;
  890.   long k = 0;
  891.   BODY *body;
  892.   int f = mail_elt (stream,i)->seen;
  893.   mail_fetchstructure (stream,i,&body);
  894.   printf ("BODY[%s] ",s);    /* output attribute */
  895.   if (body && (s = mail_fetchbody (stream,i,s,&j))) {
  896.     printf ("{%d}\015\012",j);    /* and literal string */
  897.     while (j -= k) k = fwrite (s += k,1,j,stdout);
  898.     changed_flags (i,f);    /* output changed flags */
  899.   }
  900.   else fputs ("NIL",stdout);    /* can't output anything at all */
  901. }
  902.  
  903. /* Fetch IMAP envelope
  904.  * Accepts: message number
  905.  *        string argument
  906.  */
  907.  
  908. void fetch_envelope (long i,char *s)
  909. {
  910.   ENVELOPE *env = mail_fetchstructure (stream,i,NIL);
  911.   fputs ("ENVELOPE ",stdout);    /* output attribute */
  912.   penv (env);            /* output envelope */
  913. }
  914.  
  915. /* Fetch flags
  916.  * Accepts: message number
  917.  *        string argument
  918.  */
  919.  
  920. void fetch_flags (long i,char *s)
  921. {
  922.   char tmp[MAILTMPLEN];
  923.   unsigned long u;
  924.   char *t;
  925.   MESSAGECACHE *elt = mail_elt (stream,i);
  926.   if (!elt->valid) {        /* have valid flags yet? */
  927.     sprintf (tmp,"%ld",i);
  928.     mail_fetchflags (stream,tmp);
  929.   }
  930.   s = tmp;
  931.   s[0] = s[1] = '\0';        /* start with empty flag string */
  932.                 /* output system flags */
  933.   if (elt->recent) strcat (s," \\Recent");
  934.   if (elt->seen) strcat (s," \\Seen");
  935.   if (elt->deleted) strcat (s," \\Deleted");
  936.   if (elt->flagged) strcat (s," \\Flagged");
  937.   if (elt->answered) strcat (s," \\Answered");
  938.   if (u = elt->user_flags) do    /* any user flags? */
  939.     if ((TMPLEN - ((s += strlen (s)) - tmp)) >
  940.     (2 + strlen (t = stream->user_flags[find_rightmost_bit (&u)]))) {
  941.       *s++ = ' ';        /* space delimiter */
  942.       strcpy (s,t);        /* copy the user flag */
  943.     }
  944.   while (u);            /* until no more user flags */
  945.   printf ("FLAGS (%s)",tmp+1);    /* output results, skip first char of list */
  946.   elt->spare2 = NIL;        /* we've sent the update */
  947. }
  948.  
  949.  
  950. /* Output flags if was unseen
  951.  * Accepts: message number
  952.  *        prior value of Seen flag
  953.  */
  954.  
  955. void changed_flags (long i,int f)
  956. {
  957.   if (!f) {            /* was unseen? */
  958.     putchar (' ');        /* yes, delimit with space */
  959.     fetch_flags (i,NIL);    /* output flags */
  960.   }
  961. }
  962.  
  963. /* Fetch message internal date
  964.  * Accepts: message number
  965.  *        string argument
  966.  */
  967.  
  968. void fetch_internaldate (long i,char *s)
  969. {
  970.   char tmp[MAILTMPLEN];
  971.   MESSAGECACHE *elt = mail_elt (stream,i);
  972.   if (!elt->day) {        /* have internal date yet? */
  973.     sprintf (tmp,"%ld",i);
  974.     mail_fetchfast (stream,tmp);
  975.   }
  976.   printf ("INTERNALDATE \"%s\"",mail_date (tmp,elt));
  977. }
  978.  
  979.  
  980. /* Fetch complete RFC-822 format message
  981.  * Accepts: message number
  982.  *        string argument
  983.  */
  984.  
  985. void fetch_rfc822 (long i,char *s)
  986. {
  987.   int f = mail_elt (stream,i)->seen;
  988.   printf ("RFC822 {%d}\015\012",mail_elt (stream,i)->rfc822_size);
  989.   fputs (mail_fetchheader (stream,i),stdout);
  990.   fputs (mail_fetchtext (stream,i),stdout);
  991.   changed_flags (i,f);        /* output changed flags */
  992. }
  993.  
  994.  
  995. /* Fetch RFC-822 header
  996.  * Accepts: message number
  997.  *        string argument
  998.  */
  999.  
  1000. void fetch_rfc822_header (long i,char *s)
  1001. {
  1002.   fputs ("RFC822.HEADER ",stdout);
  1003.   pstring (mail_fetchheader (stream,i));
  1004. }
  1005.  
  1006. /* Fetch RFC-822 message length
  1007.  * Accepts: message number
  1008.  *        string argument
  1009.  */
  1010.  
  1011. void fetch_rfc822_size (long i,char *s)
  1012. {
  1013.   MESSAGECACHE *elt = mail_elt (stream,i);
  1014.   if (!elt->rfc822_size) {    /* have message size yet? */
  1015.     char tmp[MAILTMPLEN];
  1016.     sprintf (tmp,"%ld",i);
  1017.     mail_fetchfast (stream,tmp);
  1018.   }
  1019.   printf ("RFC822.SIZE %d",elt->rfc822_size);
  1020. }
  1021.  
  1022.  
  1023. /* Fetch RFC-822 text only
  1024.  * Accepts: message number
  1025.  *        string argument
  1026.  */
  1027.  
  1028. void fetch_rfc822_text (long i,char *s)
  1029. {
  1030.   int f = mail_elt (stream,i)->seen;
  1031.   fputs ("RFC822.TEXT ",stdout);
  1032.   pstring (mail_fetchtext (stream,i));
  1033.   changed_flags (i,f);        /* output changed flags */
  1034. }
  1035.  
  1036. /* Print envelope
  1037.  * Accepts: body
  1038.  */
  1039.  
  1040. void penv (ENVELOPE *env)
  1041. {
  1042.   if (env) {            /* only if there is an envelope */
  1043.                 /* disgusting MacMS kludge */
  1044.     if (mackludge) printf ("%d ",cstring (env->date) + cstring (env->subject) +
  1045.                caddr (env->from) + caddr (env->sender) +
  1046.                caddr (env->reply_to) + caddr (env->to) +
  1047.                caddr (env->cc) + caddr (env->bcc) +
  1048.                cstring (env->in_reply_to) +
  1049.                cstring (env->message_id));
  1050.     putchar ('(');        /* delimiter */
  1051.     pstring (env->date);    /* output envelope fields */
  1052.     putchar (' ');
  1053.     pstring (env->subject);
  1054.     putchar (' ');
  1055.     paddr (env->from);
  1056.     putchar (' ');
  1057.     paddr (env->sender);
  1058.     putchar (' ');
  1059.     paddr (env->reply_to);
  1060.     putchar (' ');
  1061.     paddr (env->to);
  1062.     putchar (' ');
  1063.     paddr (env->cc);
  1064.     putchar (' ');
  1065.     paddr (env->bcc);
  1066.     putchar (' ');
  1067.     pstring (env->in_reply_to);
  1068.     putchar (' ');
  1069.     pstring (env->message_id);
  1070.     putchar (')');        /* end of envelope */
  1071.   }
  1072.   else fputs ("NIL",stdout);    /* no envelope */
  1073. }
  1074.  
  1075. /* Print body
  1076.  * Accepts: body
  1077.  */
  1078.  
  1079. void pbody (BODY *body)
  1080. {
  1081.   if (body) {            /* only if there is a body */
  1082.     PARAMETER *param;
  1083.     PART *part;
  1084.     putchar ('(');        /* delimiter */
  1085.                 /* multipart type? */
  1086.     if (body->type == TYPEMULTIPART) {
  1087.                 /* print each part */
  1088.       if (part = body->contents.part)
  1089.     for (; part; part = part->next) pbody (&(part->body));
  1090.       else fputs ("(\"TEXT\" \"PLAIN\" NIL NIL NIL \"7BIT\" 0 0)",stdout);
  1091.       putchar (' ');        /* space delimiter */
  1092.       pstring (body->subtype);    /* and finally the subtype */
  1093.     }
  1094.     else {            /* non-multipart body type */
  1095.       pstring ((char *) body_types[body->type]);
  1096.       putchar (' ');
  1097.       pstring (body->subtype);
  1098.       if (param = body->parameter) {
  1099.     fputs (" (",stdout);
  1100.     do {
  1101.       pstring (param->attribute);
  1102.       putchar (' ');
  1103.       pstring (param->value);
  1104.       if (param = param->next) putchar (' ');
  1105.     } while (param);
  1106.     fputs (") ",stdout);
  1107.       }
  1108.       else fputs (" NIL ",stdout);
  1109.       pstring (body->id);
  1110.       putchar (' ');
  1111.       pstring (body->description);
  1112.       putchar (' ');
  1113.       pstring ((char *) body_encodings[body->encoding]);
  1114.       printf (" %d",body->size.bytes);
  1115.       switch (body->type) {    /* extra stuff depends upon body type */
  1116.       case TYPEMESSAGE:
  1117.                 /* can't do this if not RFC822 */
  1118.     if (strcmp (body->subtype,"RFC822")) break;
  1119.     putchar (' ');
  1120.     penv (body->contents.msg.env);
  1121.     putchar (' ');
  1122.     pbody (body->contents.msg.body);
  1123.       case TYPETEXT:
  1124.     printf (" %d",body->size.lines);
  1125.     break;
  1126.       default:
  1127.     break;
  1128.       }
  1129.     }
  1130.     putchar (')');        /* end of body */
  1131.   }
  1132.   else fputs ("NIL",stdout);    /* no body */
  1133. }
  1134.  
  1135. /* Print string
  1136.  * Accepts: string
  1137.  */
  1138.  
  1139. void pstring (char *s)
  1140. {
  1141.   char c,*t;
  1142.   if (s) {            /* is there a string? */
  1143.                 /* must use literal string */
  1144.     if (strpbrk (s,"\012\015\"%{\\")) {
  1145.       printf ("{%d}\015\012",strlen (s));
  1146.       fputs (s,stdout);        /* don't merge this with the printf() */
  1147.     }
  1148.     else {            /* may use quoted string */
  1149.       putchar ('"');        /* don't even think of merging this into a */
  1150.       fputs (s,stdout);        /*  printf().  Cretin VAXen can't do a */
  1151.       putchar ('"');        /*  printf() of godzilla strings! */
  1152.     }
  1153.   }
  1154.   else fputs ("NIL",stdout);    /* empty string */
  1155. }
  1156.  
  1157.  
  1158. /* Print address list
  1159.  * Accepts: address list
  1160.  */
  1161.  
  1162. void paddr (ADDRESS *a)
  1163. {
  1164.   if (a) {            /* have anything in address? */
  1165.     putchar ('(');        /* open the address list */
  1166.     do {            /* for each address */
  1167.       putchar ('(');        /* open the address */
  1168.       pstring (a->personal);    /* personal name */
  1169.       putchar (' ');
  1170.       pstring (a->adl);        /* at-domain-list */
  1171.       putchar (' ');
  1172.       pstring (a->mailbox);    /* mailbox */
  1173.       putchar (' ');
  1174.       pstring (a->host);    /* domain name of mailbox's host */
  1175.       putchar (')');        /* terminate address */
  1176.     } while (a = a->next);    /* until end of address */
  1177.     putchar (')');        /* close address list */
  1178.   }
  1179.   else fputs ("NIL",stdout);    /* empty address */
  1180. }
  1181.  
  1182. /* Count string and space afterwards
  1183.  * Accepts: string
  1184.  * Returns: 1 plus length of string
  1185.  */
  1186.  
  1187. long cstring (char *s)
  1188. {
  1189.   char tmp[MAILTMPLEN];
  1190.   long i = s ? strlen (s) : 0;
  1191.   if (s) {            /* is there a string? */
  1192.                 /* must use literal string */
  1193.     if (strpbrk (s,"\012\015\"%{\\")) {
  1194.       sprintf (tmp,"{%d}\015\012",i);
  1195.       i += strlen (tmp);
  1196.     }
  1197.     else i += 2;        /* quoted string */
  1198.   }
  1199.   else i += 3;            /* NIL */
  1200.   return i + 1;            /* return string plus trailing space */
  1201. }
  1202.  
  1203.  
  1204. /* Count address list and space afterwards
  1205.  * Accepts: address list
  1206.  */
  1207.  
  1208. long caddr (ADDRESS *a)
  1209. {
  1210.   long i = 3;            /* open, close, and space */
  1211.                 /* count strings in address list */
  1212.   if (a) do i += 1 + cstring (a->personal) + cstring (a->adl) +
  1213.     cstring (a->mailbox) + cstring (a->host);
  1214.   while (a = a->next);        /* until end of address */
  1215.   else i = 4;            /* NIL plus space */
  1216.   return i;            /* return the count */
  1217. }
  1218.  
  1219. /* Co-routines from MAIL library */
  1220.  
  1221.  
  1222. /* Message matches a search
  1223.  * Accepts: MAIL stream
  1224.  *        message number
  1225.  */
  1226.  
  1227. void mm_searched (MAILSTREAM *s,long msgno)
  1228. {
  1229.                 /* nothing to do here */
  1230. }
  1231.  
  1232.  
  1233. /* Message exists (i.e. there are that many messages in the mailbox)
  1234.  * Accepts: MAIL stream
  1235.  *        message number
  1236.  */
  1237.  
  1238. void mm_exists (MAILSTREAM *s,long number)
  1239. {
  1240.   if (s != tstream) {        /* note change in number of messages */
  1241.     printf ("* %d EXISTS\015\012",(nmsgs = number));
  1242.     recent = -1;        /* make sure fetch new recent count */
  1243.   }
  1244. }
  1245.  
  1246.  
  1247. /* Message expunged
  1248.  * Accepts: MAIL stream
  1249.  *        message number
  1250.  */
  1251.  
  1252. void mm_expunged (MAILSTREAM *s,long number)
  1253. {
  1254.   if (s != tstream) printf ("* %d EXPUNGE\015\012",number);
  1255. }
  1256.  
  1257.  
  1258. /* Message status changed
  1259.  * Accepts: MAIL stream
  1260.  *        message number
  1261.  */
  1262.  
  1263. void mm_flags (MAILSTREAM *s,long number)
  1264. {
  1265.   if (s != tstream) mail_elt (s,number)->spare2 = T;
  1266. }
  1267.  
  1268. /* Mailbox found
  1269.  * Accepts: Mailbox name
  1270.  */
  1271.  
  1272. void mm_mailbox (char *string)
  1273. {
  1274.   printf ("* MAILBOX %s\015\012",string);
  1275. }
  1276.  
  1277.  
  1278. /* BBoard found
  1279.  * Accepts: BBoard name
  1280.  */
  1281.  
  1282. void mm_bboard (char *string)
  1283. {
  1284.   printf ("* BBOARD %s\015\012",string);
  1285. }
  1286.  
  1287. /* Notification event
  1288.  * Accepts: MAIL stream
  1289.  *        string to log
  1290.  *        error flag
  1291.  */
  1292.  
  1293. void mm_notify (MAILSTREAM *s,char *string,long errflg)
  1294. {
  1295.   char tmp[MAILTMPLEN];
  1296.   if (!tstream || (s != tstream)) switch (errflg) {
  1297.   case NIL:            /* information message, set as OK response */
  1298.     tmp[11] = '\0';        /* see if TRYCREATE for MacMS kludge */
  1299.     if (!strcmp (ucase (strncpy (tmp,string,11)),"[TRYCREATE]")) trycreate = T;
  1300.   case PARSE:            /* parse glitch, output unsolicited OK */
  1301.     printf ("* OK %s\015\012",string);
  1302.     break;
  1303.   case WARN:            /* warning, output unsolicited NO (kludge!) */
  1304.     printf ("* NO %s\015\012",string);
  1305.     break;
  1306.   case ERROR:            /* error that broke command */
  1307.   default:            /* default should never happen */
  1308.     printf ("* BAD %s\015\012",string);
  1309.     break;
  1310.   }
  1311. }
  1312.  
  1313. /* Log an event for the user to see
  1314.  * Accepts: string to log
  1315.  *        error flag
  1316.  */
  1317.  
  1318. void mm_log (char *string,long errflg)
  1319. {
  1320.   switch (errflg) {        /* action depends upon the error flag */
  1321.   case NIL:            /* information message, set as OK response */
  1322.     if (response == win) {    /* only if no other response yet */
  1323.       response = altwin;    /* switch to alternative win message */
  1324.       if (lsterr) fs_give ((void **) &lsterr);
  1325.       lsterr = cpystr (string);    /* copy string for later use */
  1326.     }
  1327.     break;
  1328.   case PARSE:            /* parse glitch, output unsolicited OK */
  1329.     printf ("* OK [PARSE] %s\015\012",string);
  1330.     break;
  1331.   case WARN:            /* warning, output unsolicited NO (kludge!) */
  1332.     if (strcmp (string,"Mailbox is empty")) printf ("* NO %s\015\012",string);
  1333.     break;
  1334.   case ERROR:            /* error that broke command */
  1335.   default:            /* default should never happen */
  1336.     response = lose;        /* set fatality */
  1337.     if (lsterr) fs_give ((void **) &lsterr);
  1338.     lsterr = cpystr (string);    /* note last error */
  1339.     break;
  1340.   }
  1341. }
  1342.  
  1343.  
  1344. /* Log an event to debugging telemetry
  1345.  * Accepts: string to log
  1346.  */
  1347.  
  1348. void mm_dlog (char *string)
  1349. {
  1350.   mm_log (string,WARN);        /* shouldn't happen normally */
  1351. }
  1352.  
  1353. /* Get user name and password for this host
  1354.  * Accepts: host name
  1355.  *        where to return user name
  1356.  *        where to return password
  1357.  *        trial count
  1358.  */
  1359.  
  1360. void mm_login (char *host,char *username,char *password,long trial)
  1361. {
  1362.   strcpy (username,user);    /* set user name */
  1363.   strcpy (password,pass);    /* and password */
  1364. }
  1365.  
  1366. /* About to enter critical code
  1367.  * Accepts: stream
  1368.  */
  1369.  
  1370. void mm_critical (MAILSTREAM *s)
  1371. {
  1372.   /* Not doing anything here for now */
  1373. }
  1374.  
  1375.  
  1376. /* About to exit critical code
  1377.  * Accepts: stream
  1378.  */
  1379.  
  1380. void mm_nocritical (MAILSTREAM *s)
  1381. {
  1382.   /* Not doing anything here for now */
  1383. }
  1384.  
  1385.  
  1386. /* Disk error found
  1387.  * Accepts: stream
  1388.  *        system error code
  1389.  *        flag indicating that mailbox may be clobbered
  1390.  * Returns: abort flag
  1391.  */
  1392.  
  1393. long mm_diskerror (MAILSTREAM *s,long errcode,long serious)
  1394. {
  1395.   char tmp[MAILTMPLEN];
  1396.   if (serious) {        /* try your damnest if clobberage likely */
  1397.     fputs ("* BAD Retrying to fix probable mailbox damage!\015\012",stdout);
  1398.     fflush (stdout);        /* dump output buffer */
  1399.     syslog (LOG_ALERT,
  1400.         "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
  1401.         user,tcp_clienthost (tmp),
  1402.         (stream && stream->mailbox) ? stream->mailbox : "???",
  1403.         strerror (errcode));
  1404.     alarm (0);            /* make damn sure timeout disabled */
  1405.     sleep (60);            /* give it some time to clear up */
  1406.     return NIL;
  1407.   }
  1408.                 /* otherwise die before more damage is done */
  1409.   printf ("* BYE Aborting due to disk error %s\015\012",strerror (errcode));
  1410.   syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
  1411.       user,tcp_clienthost (tmp),
  1412.       (stream && stream->mailbox) ? stream->mailbox : "???",
  1413.       strerror (errcode));
  1414.   return T;
  1415. }
  1416.  
  1417.  
  1418. /* Log a fatal error event
  1419.  * Accepts: string to log
  1420.  */
  1421.  
  1422. void mm_fatal (char *string)
  1423. {
  1424.   char tmp[MAILTMPLEN];
  1425.   printf ("* BYE [ALERT] IMAP2bis server crashing: %s\015\012",string);
  1426.   syslog (LOG_ALERT,"Fatal error user=%.80s host=%.80s mbx=%.80s: %.80s",
  1427.       user ? user : "???",tcp_clienthost (tmp),
  1428.       (stream && stream->mailbox) ? stream->mailbox : "???",string);
  1429. }
  1430.